// Vertex Object
var Vertex = function (x = 0, y = 0, z = 0) {
    {//构造部分
        this.x = x;
        this.y = y;
        this.z = z;
    }
    this.addForArr = function (arr) {   //vertexColors
        if (arr.length < 3) return;
        this.x = arr[0];
        this.y = arr[1];
        this.z = arr[2];
        return this;
    }

}
//TexCoords object
var TexCoords = function (s = 0, t = 0) {
    this.s = s;
    this.t = t;
}
// Face Object
var Face = function (materialName = null) {
    this.materialName = materialName;
    if (materialName == null) this.materialName = "";
    this.vIndices = new Array(0);
    this.normal = new Vector3([0,1,0]); //法向量
    this.tIndices = new Array(0);
}
//画圆柱体 obj
var HOBJ = function () {
    { //构造部分
        this.Vertexs = new Array(0);
        this.TexCoordsArr = new Array(0);
        this.faces = new Array(0);
        this.Kd = [0.4, 0.9, 0.4];   //固有色
    }

    /**scale 设置模型缩放 */
    this.PrintObj = function (scale = 1) {
        var content = `# heziwen WebGL Test \nmtllib 3DOBJ.mtl\no 3DOBJ\n`;
        //打印顶点信息
        for (var i = 0; i < this.Vertexs.length; i++) {
            var v = this.Vertexs[i];
            content += `v ${v.x * scale} ${v.y * scale} ${v.z * scale}\n`;
        }
        content += `usemtl none\n`;
        //打印面信息
        for (var i = 0; i < this.faces.length; i++) {
            var f = this.faces[i];
            content += `f ${f.vIndices[0]} ${f.vIndices[1]} ${f.vIndices[2]}\n`;
        }
        console.log(content);
        return content;
    }
    //打印mtl
    this.PrintMtl = function () {
        var content = `# heziwen WebGL Test \nnewmtl none\nKd ${this.Kd[0]} ${this.Kd[1]} ${this.Kd[2]}\n`;

        console.log(content);
        return content;
    }

    // //获得圆形的顶点
    this.creatCylinderVs = function (r, n, height, centerp) {
        if (n < 3) return;
        var v_index = 0;  //顶点索引位置
        var vcArr = new Array();


        //底部圆点
        addColor(centerp, 1.0, 0.0, 0.0); //补上颜色
        this.Vertexs.push(new Vertex().addForArr(centerp));  //记录顶点

        //
        var vs = new Array();
        var cell = Math.PI * 2 / n;
        var ty = centerp[1];
        //底部圆 顶点生成
        for (var i = 0; i < n; i++) {
            vs[i] = new Array();
            vs[i][0] = Math.sin(i * cell) * r;  //x
            vs[i][1] = ty;  //y
            vs[i][2] = Math.cos(i * cell) * r;  //z
            addColor(vs[i]);   //补上颜色
            this.Vertexs.push(new Vertex().addForArr(vs[i]));  //记录顶点

        }
        //建立 三角渲染序列
        for (var i = 0; i < vs.length; i++) {
            vcArr = vcArr.concat(centerp, vs[i], vs[(i + 1) % vs.length]);
            //记录面信息
            var tFace = new Face('none');
            tFace.normal = calcNormalVec(centerp, vs[i], vs[(i + 1) % vs.length]);
            tFace.vIndices = [1, i + 2, (i + 1) % vs.length + 2];
            this.faces.push(tFace);
        }
        //顶部圆 顶点
        centerp[1] += height;
        this.Vertexs.push(new Vertex().addForArr(centerp));  //记录顶点

        var vs_1 = new Array();
        var ty = centerp[1];
        //底部圆 顶点生成
        for (var i = 0; i < n; i++) {
            vs_1[i] = new Array();
            vs_1[i][0] = Math.sin(i * cell) * r;  //x
            vs_1[i][1] = ty;  //y
            vs_1[i][2] = Math.cos(i * cell) * r;  //z
            addColor(vs_1[i]);   //补上颜色
            this.Vertexs.push(new Vertex().addForArr(vs_1[i]));  //记录顶点

        }
        var Cnum = n + 1;   //每个圆顶点 总个数
        //建立 三角渲染序列
        for (var i = 0; i < vs_1.length; i++) {
            vcArr = vcArr.concat(centerp, vs_1[i], vs_1[(i + 1) % vs.length]);
            //记录面信息
            var tFace = new Face('none');
            tFace.normal = calcNormalVec(centerp, vs_1[i], vs_1[(i + 1) % vs.length]);
            tFace.vIndices = [1 + Cnum, i + 2 + Cnum, (i + 1) % vs.length + 2 + Cnum];
            this.faces.push(tFace);
        }

        //组建圆边的三角序列
        for (var i = 0; i < vs_1.length; i++) {
            vcArr = vcArr.concat(vs[i], vs_1[i], vs[(i + 1) % vs.length]);
            vcArr = vcArr.concat(vs_1[i], vs_1[(i + 1) % vs.length], vs[(i + 1) % vs.length]);
            //记录面信息 
            var tFace = new Face('none');
            tFace.normal= calcNormalVec(vs[i], vs_1[i], vs[(i + 1) % vs.length]);
            tFace.vIndices = [i + 2, i + 2 + Cnum, (i + 1) % vs.length + 2];
            this.faces.push(tFace);
            //记录面信息
            var tFace = new Face('none');
            tFace.normal = calcNormalVec(vs_1[i], vs_1[(i + 1) % vs.length], vs[(i + 1) % vs.length]);
            tFace.vIndices = [i + 2 + Cnum, (i + 1) % vs.length + 2 + Cnum, (i + 1) % vs.length + 2];
            this.faces.push(tFace);
        }

        this.setTexCoords();
        return vcArr;

    };

    

    //设置映射纹理ts（UV）坐标
    this.setTexCoords = function () {
        //创建纹理 坐标
        this.TexCoordsArr[0] = new TexCoords(0.0, 0.0);
        this.TexCoordsArr[1] = new TexCoords(1.0, 0.0);
        this.TexCoordsArr[2] = new TexCoords(0.0, 1.0);

        var fs = this.faces;
        for (var i = 0; i < fs.length; i++) {
            fs[i].tIndices[0] = 1;
            fs[i].tIndices[1] = 2;
            fs[i].tIndices[2] = 3;
        }
    }

    //获取纹理数组
    this.getTexs = function () {
        var rarr = new Array(0);
        var fs = this.faces;
        for (var i = 0; i < fs.length; i++) {
            for (var j = 0; j < fs[i].tIndices.length; j++) {
                var ind = fs[i].tIndices[j];
                rarr.push(this.TexCoordsArr[ind - 1].s);
                rarr.push(this.TexCoordsArr[ind - 1].t);
            }
        }

        return rarr;
    }

};

//根据 三个定点 计算法向量
function calcNormalVec(v_a,v_b,v_c){
    if(!v_a || !v_b ||!v_c) return;
    var aE = v_a.elements;
    var bE = v_b.elements;
    var cE = v_c.elements;
    var v_AB = new Vector3([v_b[0]-v_a[0],v_b[1]-v_a[1],v_b[2]-v_a[2]]);
    var v_BC = new Vector3([v_c[0]-v_b[0],v_c[1]-v_b[1],v_c[2]-v_b[2]]);

    var rsu = v_AB.Cross(v_BC);
    rsu.normalize();
    return rsu;
}

//顶点着色程序
var VSHADER_SOURCE =
    `attribute vec4 a_Pos;\n` +
    `attribute vec4 a_Color;\n ` +
    `attribute vec2 a_TexCoord;\n` +
    `attribute vec4 a_Normal;\n` +
    'uniform mat4 u_ModelMatrix;\n' +
    'uniform mat4 u_ViewMatrix;\n' +
    'uniform mat4 u_ProjMatrix;\n' +
    'uniform vec3 u_LightColor;\n' +
    'uniform vec3 u_LightDirection;\n' +
    `varying vec4 v_Color;\n` +
    `varying vec2 v_TexCoord;\n` +
    `void main() {\n` +
    `gl_Position = u_ProjMatrix*u_ViewMatrix*u_ModelMatrix *a_Pos;\n` +

    `vec3 normal = normalize(vec3(a_Normal)); \n` +
    `float nDotL = max(dot(u_LightDirection,normal),0.0); \n` +
    `vec4 tempColor = vec4(1.0,1.0,1.0,1.0); \n` +
    `vec3 diffuse = u_LightColor * vec3(tempColor) * nDotL; \n` +
    `v_Color = vec4(diffuse,tempColor.a); \n` +
    `v_TexCoord = a_TexCoord;\n` +
    `}\n`

//片元着色器
var FSHADER_SOURCE =
    '#ifdef GL_ES\n' +
    'precision mediump float;\n' +
    '#endif\n' +
    `varying  vec4 v_Color;\n` +
    `uniform sampler2D u_Sampler;\n` +
    `varying vec2 v_TexCoord;\n` +
    `void main(){\n` +
    `vec4 uvColor = texture2D(u_Sampler,v_TexCoord); \n`+
    `gl_FragColor = vec4(vec3(uvColor * v_Color),1.0) ;\n` +
    `}\n`

function main() {
    //获取 canvas 元素
    var canvas = document.getElementById("webGL");
    if (!canvas) {
        console.log(` failed to retrieve the<canvas> element`);
        return;
    }
    //获取nearFar 元素
    var nf = document.getElementById(`nearFar`);
    if (!nf) {
        console.error(`获取 neraFar 元素failed`);
        return;
    }

    //获取 绘制二维图形 上下文 context
    var gl = getWebGLContext(canvas);
    if (!gl) {
        console.log(`WebGl 的上下文环境获取失败`);
        return;
    }

    //初始化着色器
    if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
        console.error(`failed to initialize shaders`);
        return;
    }


    //mvp矩阵 地址获取
    var u_M_Matrixdz = gl.getUniformLocation(gl.program, `u_ModelMatrix`);
    var u_V_Matrixdz = gl.getUniformLocation(gl.program, `u_ViewMatrix`);
    var u_P_Matrixdz = gl.getUniformLocation(gl.program, `u_ProjMatrix`);
    //灯光色地址
    var u_LightColor = gl.getUniformLocation(gl.program,`u_LightColor`);
    var u_LightDirection = gl.getUniformLocation(gl.program,`u_LightDirection`);

    if (!u_M_Matrixdz || !u_V_Matrixdz || !u_P_Matrixdz || !u_LightColor || !u_LightDirection) {
        console.error(`地址获取失败`);
        return;
    }

    //设置所有的定点
    var n = initVertexBuffers(gl);
    if (n < 0) {
        console.error(`failed 设置 顶点`);
        return;
    }

    //配置纹理
    if (!initTextures(gl, n)) {
        console.error(`failed 纹理 配置`);
        return;
    }

    //mvp set
    var m_Matrix = new Matrix4();
    var v_Matrix = new Matrix4();
    var p_Matrix = new Matrix4();

    m_Matrix.setRotate(0, 0, 0, 1);   //M
    v_Matrix.setLookAt(0.0, 0.0, 3.0, 0, 0, 0, 0, 1, 0);//V       //MixMatrix.setLookAt(0.20, 0.25, 0.25, 0, 0, 0, 0, 1, 0);
    p_Matrix.setPerspective(60, canvas.width / canvas.height, 0.006, 500);//P 投影

    // 将视图矩阵传给 u_ViewMatrix 变量
    gl.uniformMatrix4fv(u_M_Matrixdz, false, m_Matrix.elements);
    gl.uniformMatrix4fv(u_V_Matrixdz, false, v_Matrix.elements);
    gl.uniformMatrix4fv(u_P_Matrixdz, false, p_Matrix.elements);

    //灯光
    gl.uniform3f(u_LightColor,1.0,1.0,1.0);
    var lightDirection = new Vector3([0.5,0.3,0.4]);
    lightDirection.normalize();
    gl.uniform3fv(u_LightDirection,lightDirection.elements);

    //操作指定地址中的数据
    gl.clearColor(0.1, 0.1, 0.1, 1.0);
    //开启 消除隐藏面
    gl.enable(gl.DEPTH_TEST);

    gl.clear(gl.COLOR_BUFFER_BIT);

    document.onkeydown = (ev) => {
        if (TextureLoaded)
            keyDown(ev, gl, n, u_M_Matrixdz, m_Matrix);
    }


}  //main end 


//----------------------------------------------华丽的分割线-----------------------------
var g_Points = [];  //点击左边数组
var g_colors = [];

var z_rot = 0, y_rot = 0; // Eye position
function keyDown(ev, gl, n, u_m_Matrix, m_matrix) {
    switch (ev.keyCode) {
        case 39: z_rot += 1.0; break;
        case 37: z_rot -= 1.0; break;
        case 38: y_rot += 1.0; break;
        case 40: y_rot -= 1.0; break;
        default: return;
    }
    DoDraw(gl, n, u_m_Matrix, m_matrix);
}

function DoDraw(gl, n, u_m_Matrix, m_matrix) {
    var y_m = new Matrix4().setRotate(y_rot, 1, 0, 0);
    var z_m = new Matrix4().setRotate(z_rot, 0, 0, 1);
    m_matrix = y_m.multiply(m_matrix);
    m_matrix = z_m.multiply(m_matrix);

    // Pass the view projection matrix
    gl.uniformMatrix4fv(u_m_Matrix, false, m_matrix.elements);

    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);     // Clear <canvas>

    gl.drawArrays(gl.TRIANGLES, 0, n);
}
//保存文件到本地
function DoSave() {
    if (hobj == null) return;
    var content_obj = hobj.PrintObj(50);
    var content_mtl = hobj.PrintMtl();

    var file = new File([content_obj], "3DOBJ.obj", { type: "text/plain;charset=utf-8" });
    saveAs(file);
    var file = new File([content_mtl], "3DOBJ.mtl", { type: "text/plain;charset=utf-8" });
    saveAs(file);
}

function addColor(arr, r = 0.4, g = 1.0, b = 0.4) {
    arr[3] = r;
    arr[4] = g;
    arr[5] = b;
}

var xs = 10;  //调试系数

var hobj = new HOBJ();
var cArr = hobj.creatCylinderVs(0.4, 8, 1, [0.0, 0.0, -0.0 * xs]);
var tarr = hobj.getTexs();

//--------------------------------------
//绘制缓冲区 点数组
function initVertexBuffers(gl) {
    var cellLen = 9;
    var verticesColors = new Float32Array( hobj.faces.length * 3 *cellLen);
    var texs = new Float32Array( tarr.length);
   

    let faces = hobj.faces;
    let CAllBuffer = new Array();
    for(var i=0; i<faces.length ;i++){
        let vidxs = faces[i].vIndices;
        let nomal = faces[i].normal;
       for(var j=0;j<vidxs.length;j++){
            let idx = vidxs[j]-1;
            let vt = hobj.Vertexs;
            let el = nomal.elements;
            CAllBuffer.push(vt[idx].x,vt[idx].y,vt[idx].z ,1,1,1,el[0],el[1],el[2] );
       }         
    }

    verticesColors.set(CAllBuffer, 0);

    texs.set(tarr, 0);   //纹理坐标


    var n = verticesColors.length / cellLen;  //point conut


    //创建缓冲区对象
    var vertexColorbuffer = gl.createBuffer();
    if (!vertexColorbuffer) {
        console.error(`failed to create the buffer obj`);
        return -1;
    }

    //将缓冲区对象绑定到目标
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorbuffer);

    //向缓冲区对象中写入数据、
    gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);
    //元素单位长度获取
    var FSIZE = verticesColors.BYTES_PER_ELEMENT;
    //pos 数据设置
    var a_Posdz = gl.getAttribLocation(gl.program, `a_Pos`);
    if (a_Posdz < 0) {
        console.error(`地址获取失败`);
        return -1;
    }
    //缓冲区对象 分配到 a_Posdz 
    gl.vertexAttribPointer(a_Posdz, 3, gl.FLOAT, false, FSIZE * cellLen, 0);
    //连接 a_Posdz 与获得的缓冲区对象
    gl.enableVertexAttribArray(a_Posdz);

    //法向量
    var a_Normaldz = gl.getAttribLocation(gl.program, `a_Normal`);
    if (a_Normaldz < 0) {
        console.error(`地址获取失败`);
        return -1;
    }
    //缓冲区对象 分配到 a_Normal 
    gl.vertexAttribPointer(a_Normaldz, 3, gl.FLOAT, false, FSIZE * cellLen, FSIZE*6);
    //连接 a_Normal 与获得的缓冲区对象
    gl.enableVertexAttribArray(a_Normaldz);

    // //定点颜色
    // var a_Colordz = gl.getAttribLocation(gl.program,`a_Color`);
    // if(a_Colordz <0){
    // console.error(`地址获取失败`);
    //     return -1;
    // }
    // //顶点属性指针 设置 color
    // gl.vertexAttribPointer(a_Colordz,3,gl.FLOAT,false,FSIZE*6,FSIZE*3);
    // //开启使用
    // gl.enableVertexAttribArray(a_Colordz);


    var texsBuffer = gl.createBuffer();
    if (!texsBuffer) {
        console.error(`failed to create the buffer obj`);
        return -1;
    }
    //绑定缓冲区对象到目标
    gl.bindBuffer(gl.ARRAY_BUFFER, texsBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, texs, gl.STATIC_DRAW);

    //元素单位长度获取
    var FSIZE_1 = texs.BYTES_PER_ELEMENT;
    //纹理坐标
    var a_TexCoord = gl.getAttribLocation(gl.program, `a_TexCoord`);
    if (a_TexCoord < 0) {
        console.error(`地址获取失败`);
        return -1;
    }

    gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, FSIZE_1 * 2, 0);
    gl.enableVertexAttribArray(a_TexCoord);


    return n;
}

//---------------
//初始化贴图纹理
function initTextures(gl, n) {
    var texture = gl.createTexture();  //创建了纹理对象
    //获取 u_Sampler 的地址
    var u_Sampler = gl.getUniformLocation(gl.program, `u_Sampler`);
    if (u_Sampler < 0) {
        console.error(`failed location get `);
        return;
    }
    //创建image对象
    var image = new Image();
    image.onload = function () { loadTexture(gl, n, texture, u_Sampler, image); };   //加载回调

    image.src = `./tex_black_wite.jpg`;

    return true;
}

var TextureLoaded = false;
//----------------
//加载 纹理
function loadTexture(gl, n, texture, u_Sampler, image) {
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); //对纹理图像Y轴反转 
    gl.activeTexture(gl.TEXTURE0);  //开启0号纹理单元
    gl.bindTexture(gl.TEXTURE_2D, texture); //绑定纹理对象到TEXTURE_2D
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); //配置纹理参数
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image); //配置纹理图像
    gl.uniform1i(u_Sampler, 0);  //将0号纹理单元 传递给着色器

    TextureLoaded = true;  //加载完成标识

    //开始绘制
    gl.drawArrays(gl.TRIANGLES, 0, n);    // 开始绘制三角形

}


